/**
 * 基于Promise.race配合Promise.all实现异步并发控制
 */

async function taskAsyncPool(taskList, iteratorFn, limit = 1) {
  const ret = [] // 存放全部的Promise对象
  const executing = [] // 存放并发限制的处于Pending状态的Promise
  for (const item of taskList) {
    const p = Promise.resolve().then(() => iteratorFn(item))
    ret.push(p)
    if (limit <= taskList.length) { // 分片数量超出限制，进行并发限制
      const e = p.then(() => executing.splice(executing.indexOf(e), 1)) // resolve后移除数组
      executing.push(e)
      if (executing.length >= limit) { // 超过限制请求数时，主动卡住，等待之前的请求结束
        await Promise.race(executing) // 响应成功后，继续下一项循环
      }
    }
  }
  return Promise.all(ret)
}

/**
 * 基于队列配合Promise实现异步并发限制
 */

// 队列
class Queue {
  constructor() {
    this._queue = [];
  }
  push(value) {
    return this._queue.push(value);
  }
  shift() {
    // TODO 优化出队操作 shift 操作的时间复杂度为 O(n)。使用 reverse + pop 的方式，引入双数组的设计，减低时间复杂度类O(1)
    return this._queue.shift();
  }
  isEmpty() {
    return this._queue.length === 0;
  }
}

// 延迟任务
class DelayedTask {
  constructor(resolve, fn, args) {
    this.resolve = resolve;
    this.fn = fn;
    this.args = args;
  }
}

// 任务池
class TaskPool {
  constructor(size) {
    this.size = size;
    this.queue = new Queue();
  }

  addTask(fn) {
    return (...args) => {
      return new Promise((resolve) => {
        this.queue.push(new DelayedTask(resolve, fn, args));
        if (this.size) {
          this.size--;
          const {
            resolve: taskResolve,
            fn: taskFn,
            args: taskArgs,
          } = this.queue.shift();
          taskResolve(this.runTask(taskFn, taskArgs));
        }
      });
    };
  }
  
  pullTask() {
    if (this.queue.isEmpty()) {
      return;
    }
    if (this.size === 0) {
      return;
    }
    this.size--;
    const { resolve, fn, args } = this.queue.shift();
    resolve(this.runTask(fn, args));
  }

  runTask(fn, args) {
    const result = Promise.resolve(fn(...args));
    result.finally(() => {
      this.size++;
      this.pullTask();
    });
    return result;
  }
}

/* 并发控制 */
function concurrencyRequest(urls, maxNum = 1) {
  // 验证类型
  if (!Array.isArray(urls)) {
    throw 'urls must be an array type'
  }

  return new Promise((resolve) => {
    if (!urls.length) {
      resolve([])
      return
    }

    const results = []
    let page = 0
    let count = 0

    async function req() {
      // 请求结束
      if (page === urls.length) {
        return
      }

      let index = page
      let url = urls[page]
      page++
      try {
        // 接口获取
        const resp = await axios.get(url)
        results[index] = resp
      } catch (e) {
        results[index] = e
      } finally {
        count++
        if (count === urls.length) {
          resolve(results)
        }
        req()
      }
    }

    for (let i = 0, len = Math.min(maxNum, urls.length); i < len; i++) {
      req()
    }
  })
}

/* async-pool-tiny */
async function asyncPool(poolLimit, iterable, iteratorFn) {
  const ret = []
  const executing = new Set()
  for (const item of iterable) {
    const p = Promise.resolve().then(() => iteratorFn(item, iterable))
    ret.push(p)
    executing.add(p)
    const clean = () => executing.delete(p)
    p.then(clean).catch(clean)
    if (executing.size >= poolLimit) {
      await Promise.race(executing)
    }
  }
  return Promise.all(ret)
}

/* 请求池限制 */
async function asyncPool(poolLimit, iterable) {
  // 用于保存所有异步请求
  const ret = []
  // 用于保存正在进行的请求
  const executing = new Set()
  for (const item of iterable) {
    // 构造出请求 Promise
    const p = Promise.resolve().then(() => item());
    ret.push(p);
    executing.add(p);
    // 请求执行结束后从正在机械能的数组中移除
    const clean = () => executing.delete(p);
    p.then(clean).catch(clean);
    // 如果正在执行的请求数大于并发数，就使用Promise.race等待一个最快执行完的请求
    if (executing.size >=  poolLimit) {
      await Promise.race(executing);
    }
  }

  return Promise.all(ret);
}